#ifndef __CALIBRATER_H__
#define __CALIBRATER_H__

#include <atomic>
#include <filesystem>
#include <string>

#include "plate_solver/plate_solver.h"
#include "motor_controller.h"
#include "ws_constant.h"
#include "rgb_power.h"

struct CalibraterState {
    std::atomic<int> plate_solving_times;
    std::atomic<StateNotify> state;

    CalibraterState() : plate_solving_times(0), state(STATE_IDLE) {}
};

class ITips;

class Calibrater {
private:
    AstrometryNet asNetPlateSolver_;
    PlateSolver plateSolver_;

    CamTele cam_tele_;
    RgbPower rgb_power_;

    StepMotorUser& motor_yaw_;
    StepMotorUser& motor_pitch_;

    const int motor_resolution_level_ = STEP_MOTOR_MINISTEP_8;
    const int motor_speed_ramping_ = STEP_MOTOR_RAMP_PULSE_1000;

    const int max_try_times_ = 9;
    const int need_success_times_ = 3;
    int success_times_ = 0;

    const double exposure_ = 1.0;
    const int gain_ = 80;
    const int ir_state_ = 1;// IR PASS

    const double small_rotate_angle_ = 5.0;// 上次Plate Solving成功，下次在其附近继续
    const double big_rotate_angle_ = 20.0;// 上次Plate Solving失败，下次远离这片区域重试
    const double rotate_speed_ = 32.0;

    const std::filesystem::path tmpPath = "/tmp";
    const std::filesystem::path failedPath = "/sdcard/DWARF_II/Astronomy/Solving_Failed";
    const std::string calibrationFolderPrefix = "DWARF_CALIBRATION_"; 

    static HourAngleCoordinates zenithCoord_;
    static HorizontalCoordinates lastMechanicalCoord_;
    static HorizontalCoordinates lastMyCoord_;

    static bool b_reverse_motor_yaw_mechanical_direction_;
    static bool b_reverse_motor_pitch_mechanical_direction_;

    std::atomic<bool> b_calibration = false;

    static CalibraterState calibrater_state_;

    ITips* itips_observer_;

private:
    int calculateMyZenithCoord(HourAngleCoordinates starHCoord[3], HourAngleCoordinates& zenithSphericalCoord) const;
    void changeSphericalToCartesian(FLoat p_S[2], FLoat p_D[3]) const;
    void changeCartesianToSpherical(FLoat p_D[3], FLoat p_S[2]) const;
    void getPlaneEquation(FLoat p1[3], FLoat p2[3], FLoat p3[3], FLoat result[4]) const;
    FLoat det3(FLoat a[3][3]) const;
    void transferAToB(FLoat pass[3][3], FLoat equation[3][4], int col) const;

public:
    Calibrater() : plateSolver_(&asNetPlateSolver_), motor_yaw_(MotorController::motorYaw()), motor_pitch_(MotorController::motorPitch()) {
        if (!std::filesystem::exists(failedPath)) {
            try {
                std::filesystem::create_directory(failedPath);
            } catch (const std::filesystem::filesystem_error& e) {
                printf("std::filesystem::create_directory:%s\n", e.what());
            }
        }
    }
    int start(const std::vector<HorizontalCoordinates>& calibration_path);
    int start();
    int stop();

    inline void getMyZenithCoord(HourAngleCoordinates& zenith) const { zenith = zenithCoord_; }
    inline void getLastMechanicalCoord(HorizontalCoordinates& lastMechanicalCoord) const { lastMechanicalCoord = lastMechanicalCoord_; }
    inline void getLastMyCoord(HorizontalCoordinates& lastMyCoord) const { lastMyCoord = lastMyCoord_; }

    inline void getCalibrationRotateAngle(double& calibration_rotate_angle) const { calibration_rotate_angle = small_rotate_angle_; }

    inline StateNotify getCalibraterState() const { return calibrater_state_.state; }
    inline int getSuccessTimes() const { return success_times_; }

    void setAndNotifyCalibraterState(const StateNotify& state, int plate_solving_times = 0);
    void notifyCalibraterState();

    bool getReverseMotorYawMechanicalDirection() { return b_reverse_motor_yaw_mechanical_direction_; }
    bool getReverseMotorPitchMechanicalDirection() { return b_reverse_motor_pitch_mechanical_direction_; }

    inline void addITipsObserver(ITips* itips_observer) { itips_observer_ = itips_observer; }
};



#endif